package com.free.semantic.common;


import java.util.*;
import java.util.stream.Collectors;

import static com.free.semantic.common.Range.*;

public class RelationChain {

    private List<RangeObj> sourceObjectList;

    private RangeObj destObject;

    private List<RelationItem> relationItems;

    private Map<RelationItem, RelationItem> breakPointInfo;

    private int version;

    public Set<String> getUniquePathSet() {
        Set<String> result = new HashSet<>();
        Map<String, Integer> index = new HashMap<>();
        for (RelationItem item : relationItems) {
            List<RangeObj> sourceObjList = item.getSourceObjectList();
            List<RangeObj> sorted = sourceObjList.stream().sorted(Comparator.comparing(RangeObj::getObjectName)).collect(Collectors.toList());
            StringBuilder builder = new StringBuilder();

            for (RangeObj obj : sorted) {
                String objectName = obj.getObjectName();
                index.putIfAbsent(objectName, 0);
                Integer i = index.get(objectName);
                builder.append(i);
                builder.append("-");
                builder.append(obj);
            }
            builder.append("->");
            builder.append(item.getDestObject().toString());
            result.add(builder.toString());
            String destObj = item.getDestObj();
            if (index.containsKey(destObj)) {
                index.put(destObj, index.get(destObj) + 1);
            } else {
                index.put(destObj, 0);
            }
        }
        return result;
    }

    public RelationItem toRelationItem() {
        RelationItem relationItem = new RelationItem();
        relationItem.setSourceObjectList(sourceObjectList);
        relationItem.setDestObject(destObject);
        return relationItem;
    }

    public void fixDestObjRange() {
        Map<String, RangeObj> rangeObjMap = new LinkedHashMap<>();
        for (RelationItem item : relationItems) {
            for (RangeObj rangeObj : item.getSourceObjectList()) {
                rangeObjMap.putIfAbsent(rangeObj.getObjectName(), rangeObj);
            }
            boolean fix = false;
            for (RangeObj rangeObj : item.getSourceObjectList()) {
                Range range = rangeObj.getRange();
                Range destRange = rangeObjMap.get(rangeObj.getObjectName()).getRange();
                if ((LESS_ONE.equals(range) || range.equals(ONE))
                        && (MULTIPLE.equals(destRange) || MORE_ONE.equals(destRange))) {
                    rangeObjMap.put(item.getDestObj(), item.getDestObject().withOtherRange(destRange));
                    fix = true;
                    break;
                }
            }
            if (!fix) {
                rangeObjMap.put(item.getDestObj(), item.getDestObject());
            }
        }
        this.destObject = rangeObjMap.get(this.destObject.getObjectName()).clone();
    }

    public List<RangeObj> getSourceObjectList() {
        return sourceObjectList;
    }

    public void setSourceObjectList(List<RangeObj> sourceObjectList) {
        this.sourceObjectList = sourceObjectList;
    }

    public RangeObj getDestObject() {
        return destObject;
    }

    public void setDestObject(RangeObj destObject) {
        this.destObject = destObject;
    }

    public List<RelationItem> getRelationItems() {
        return relationItems;
    }

    public void setRelationItems(List<RelationItem> relationItems) {
        this.relationItems = relationItems;
    }

    public Map<RelationItem, RelationItem> getBreakPointInfo() {
        return breakPointInfo;
    }

    public void setBreakPointInfo(Map<RelationItem, RelationItem> breakPointInfo) {
        this.breakPointInfo = breakPointInfo;
    }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }

    @Override
    public String toString() {
        return String.format("%s[%s]", toRelationItem().toString(),
                String.join(";", relationItems.stream().map(RelationItem::toString).collect(Collectors.toList())));
    }
}
